package com.chuangke.framework.service.impl;

import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.chuangke.common.db.page.CkPageQuery;
import com.chuangke.common.exception.ChuangkeException;
import com.chuangke.framework.model.LevelModel;
import com.chuangke.framework.service.TreeDictModelService;
import com.chuangke.framework.util.TreeUtils;
import org.apache.commons.collections.CollectionUtils;
import org.springframework.aop.framework.AopContext;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.transaction.annotation.Transactional;

import java.util.*;
import java.util.stream.Collectors;

import static com.chuangke.common.constant.CacheKeyConstants.DICT_CACHE;

public class TreeDictModelServiceImpl<M extends ServiceImpl<?, T>, T extends LevelModel<T>>
		implements TreeDictModelService<T> {

	@Autowired
	protected M daoService;

	@Override
	@Transactional(rollbackFor = Throwable.class)
	@CacheEvict(cacheNames = DICT_CACHE, keyGenerator = "commonCacheKeyGenerator")
	public void save(T record) {
		daoService.saveOrUpdate(record);
		level();
	}

	@Override
	@Transactional(rollbackFor = Throwable.class)
	@CacheEvict(cacheNames = DICT_CACHE, keyGenerator = "commonCacheKeyGenerator")
	public void save(List<T> records) {
		daoService.saveOrUpdateBatch(records);
		level();
	}

	/**
	 * 对数据进行分级，更新数据的level,is_last_level,data_domain
	 */
	@Transactional(rollbackFor = Throwable.class)
	@CacheEvict(cacheNames = DICT_CACHE, keyGenerator = "commonCacheKeyGenerator")
	public void level() {
		List<T> list = findTree();
		List<T> newList = new ArrayList<>();
		TreeUtils.level("", 0, list, newList);
		doUpdateBatch(newList);
	}

	@Transactional(rollbackFor = Throwable.class)
	@CacheEvict(cacheNames = DICT_CACHE, keyGenerator = "commonCacheKeyGenerator")
	public void doSave(T record) {
		daoService.saveOrUpdate(record);
	}

	@Transactional(rollbackFor = Throwable.class)
	@CacheEvict(cacheNames = DICT_CACHE, keyGenerator = "commonCacheKeyGenerator")
	public void doUpdateBatch(List<T> list) {
		daoService.updateBatchById(list);
	}

	@Transactional(rollbackFor = Throwable.class)
	@CacheEvict(cacheNames = DICT_CACHE, keyGenerator = "commonCacheKeyGenerator")
	@Override
	public void delete(T record) {
		List<T> children = findAllChildren(Collections.singletonList(record.getId()));
		List<T> allRecords = TreeUtils.treeToList(children);
		if (CollectionUtils.isEmpty(allRecords)) {
			return;
		}
		daoService.removeBatchByIds(allRecords);
	}

	@Transactional(rollbackFor = Throwable.class)
	@CacheEvict(cacheNames = DICT_CACHE, keyGenerator = "commonCacheKeyGenerator")
	@Override
	public void delete(List<T> records) {
		List<String> idList = records.stream().map(T::getId).collect(Collectors.toList());
		List<T> children = findAllChildren(idList);
		List<T> allRecords = TreeUtils.treeToList(children);
		if (CollectionUtils.isEmpty(allRecords)) {
			return;
		}
		daoService.removeBatchByIds(records);
	}

	@Transactional(rollbackFor = Throwable.class)
	@CacheEvict(cacheNames = DICT_CACHE, keyGenerator = "commonCacheKeyGenerator")
	@Override
	public void deleteByIds(List<String> ids) {
		daoService.removeByIds(ids);
	}

	@Cacheable(cacheNames = DICT_CACHE, keyGenerator = "commonCacheKeyGenerator")
	@Override
	public Map<String, T> findMap() {
		List<T> list = daoService.list();
		return list.stream().collect(Collectors.toMap(T::getId, a -> a));
	}

	@SuppressWarnings({ "rawtypes", "unchecked" })
	@Override
	public List<T> findList() {
		Map<String, T> map = ((TreeDictModelService) AopContext.currentProxy()).findMap();
		return new ArrayList<>(map.values());
	}

	@SuppressWarnings({ "rawtypes", "unchecked" })
	@Override
	public List<T> findTree() {
		List<T> depts = ((TreeDictModelService) AopContext.currentProxy()).findList();
		return TreeUtils.buildTree(depts);
	}

	@Override
	public List<T> findListWithChildren() {
		List<T> list = findTree();
		return TreeUtils.treeToList(list);
	}

	@SuppressWarnings({ "rawtypes", "unchecked" })
	@Override
	public T findById(String id) {
		if (id == null) {
			return null;
		}

		Map<String, T> map = ((TreeDictModelService) AopContext.currentProxy()).findMap();
		return map.get(id);
	}

	@Override
	public List<T> findLastLevel() {
		List<T> treeList = findTree();

		List<T> lastLevelDeptList = new ArrayList<>();
		TreeUtils.findLastLevel(treeList, lastLevelDeptList);

		return lastLevelDeptList;
	}

	@SuppressWarnings({ "rawtypes", "unchecked" })
	@Override
	public List<T> findByLevel(int level) {
		List<T> list = ((TreeDictModelService) AopContext.currentProxy()).findList();
		return TreeUtils.findByLevel(list, level);
	}

	@Override
	public List<T> findLastLevel(String deptId) {
		List<T> list = findListWithChildren();
		Map<String, T> map = list.stream().collect(Collectors.toMap(T::getId, a -> a));

		List<T> deptTreeList = new ArrayList<>();
		deptTreeList.add(map.get(deptId));

		List<T> lastLevelDeptList = new ArrayList<>();
		TreeUtils.findLastLevel(deptTreeList, lastLevelDeptList);
		return lastLevelDeptList;
	}

	@Override
	public List<T> findNextLevel(String deptId) {
		List<T> list = findListWithChildren();
		Map<String, T> map = list.stream().collect(Collectors.toMap(T::getId, a -> a));
		return map.get(deptId) == null ? null : map.get(deptId).getChildren();
	}

	@SuppressWarnings({ "rawtypes", "unchecked" })
	@Override
	public List<T> findKinshipTree(Collection<String> deptIdList) {
		Map<String, T> noChildMap = ((TreeDictModelService) AopContext.currentProxy()).findMap();
		List<T> withChildrenList = findListWithChildren();
		return TreeUtils.findKinshipTree(noChildMap, withChildrenList, deptIdList);
	}

	@SuppressWarnings({ "rawtypes", "unchecked" })
	@Override
	public T findParent(String deptId) {
		Map<String, T> noChildMap = ((TreeDictModelService) AopContext.currentProxy()).findMap();
		return TreeUtils.getParent(deptId, noChildMap);
	}

	@Override
	public List<T> findAllChildren(List<String> idList) {
		List<T> list = findListWithChildren();
		list = list.stream().filter(dept -> idList.contains(dept.getId())).collect(Collectors.toList());
		if (CollectionUtils.isEmpty(list)) {
			return new ArrayList<>();
		}
		List<T> result = new ArrayList<>();
		TreeUtils.foreachChildren(result, list);

		return result;
	}

	@Override
	public IPage<T> page(CkPageQuery page) {
		throw new ChuangkeException("暂不支持的方法");
	}

}
